#include <iostream>
#include <sys/stat.h>
#include <dirent.h>
#include "print.h"
#include "package.h"
#include "module.h"

Package::Package() :
   _root( *this )
{
}

Package::Package( Package& root, const std::string& name ) :
   _root( root ),
   _name( name )
{
}

Package::~Package()
{
}

Package& Package::root()
{
   return _root;
}

bool Package::parse( const std::string& directory )
{
   DIR* pdir = opendir( directory.c_str() );

   if( pdir == NULL )
   {
      std::cerr << "Couldn't open directory " << directory << std::endl;
      return false;
   }

   struct dirent* pfile;
   struct stat sb;
   bool success = true;

   while( (pfile = readdir( pdir )) != NULL )
   {
      if( pfile->d_name[0] == '.' )
      {
         continue;
      }

      std::string path = directory + "/" + pfile->d_name;
      stat( path.c_str(), &sb );

      if( S_ISDIR( sb.st_mode ) )
      {
         success &= add_package( path, pfile->d_name );
      } else {
         char *p = strrchr( pfile->d_name, '.' );

         if( (p == NULL) || strcasecmp( p, ".pony" ) )
         {
            continue;
         }

         success &= add_module( path, pfile->d_name );
      }
   }

   closedir( pdir );

   return success;
}

bool Package::type_check()
{
   bool success = true;

   for( uint32_t i = 0; i < _modules.size(); i++ )
   {
      success &= _modules[i]->type_check();
   }

   for( uint32_t i = 0; i < _packages.size(); i++ )
   {
      success &= _packages[i]->type_check();
   }

   return true;
}

void Package::print( uint32_t level )
{
   indent( level );
   std::cout << "package " << _name << std::endl;

   for( uint32_t i = 0; i < _modules.size(); i++ )
   {
      _modules[i]->print( level + 1 );
   }

   for( uint32_t i = 0; i < _packages.size(); i++ )
   {
      _packages[i]->print( level + 1 );
   }
}

void Package::resolve_type( const ASTBaseType& name, ASTTypeDefinition::Vector& list )
{
   uint32_t count = name.count();

   if( count == 0 )
   {
      return;
   }

   // find the component package
   Package::Ptr current( this );
   uint32_t top = count - 1;

   for( uint32_t i = 0; i < top; i++ )
   {
      const std::string& fragment = name.get( i );
      Package::Ptr next;

      for( uint32_t j = 0; j < current->_packages.size(); j++ )
      {
         Package::Ptr sub = current->_packages[j];

         if( fragment == sub->name() )
         {
            next = sub;
            break;
         }
      }

      if( !next )
      {
         return;
      }

      current = next;
   }

   current->resolve_local_type( name.get( top ), list );
}

void Package::resolve_local_type( const std::string& name, ASTTypeDefinition::Vector& list )
{
   // all types of this name that are local to this package
   for( uint32_t i = 0; i < _modules.size(); i++ )
   {
      _modules[i]->resolve_local_type( name, list );
   }
}

bool Package::add_package( const std::string& path, const std::string& name )
{
   Package::Ptr package( new Package( _root, name ) );
   bool success = package->parse( path );

   if( package->has_modules() )
   {
      _packages.push_back( package );
      return success;
   }

   return true;
}

bool Package::add_module( const std::string& path, const std::string& name )
{
   Module::Ptr module( new Module( *this, name, path ) );
   _modules.push_back( module );

   return module->parse();
}

bool Package::has_modules()
{
   if( _modules.size() > 0 )
   {
      return true;
   }

   for( uint32_t i = 0; i < _packages.size(); i++ )
   {
      if( _packages[i]->has_modules() )
      {
         return true;
      }
   }

   return false;
}
